package com.zzedu.framework.aspectj;

import com.zzedu.common.annotation.DataScope;
import com.zzedu.common.core.domain.entity.SysRole;
import com.zzedu.common.core.domain.entity.SysUser;
import com.zzedu.common.enums.CommonEnums;
import com.zzedu.common.enums.UserTypeEnumns;
import com.zzedu.common.utils.SecurityUtils;
import com.zzedu.common.utils.StringUtils;
import com.zzedu.common.utils.security.LoginUser;
import com.zzedu.framework.interceptor.DataFilterScopeContext;
import com.zzedu.system.service.ISysRoleService;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.commons.compress.utils.Lists;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

@Aspect
@Component
public class DataScopeAspect {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 数据权限类型常量
    /**
     * 所有数据
     */
    public static final String DATA_SCOPE_ALL = "1";
    /**
     * 自定义数据
     */
    public static final String DATA_SCOPE_CUSTOM = "2";
    /**
     * 部门数据
     */
    public static final String DATA_SCOPE_DEPT = "3";
    /**
     * 部门及以下数据
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
    /**
     * 本人数据
     */
    public static final String DATA_SCOPE_SELF = "5";
    /**
     * 本人学校数据
     */
    public static final String DATA_SCOPE_SELF_SCHOOL = "6";
    /**
     * 本人机构数据
     */
    public static final String DATA_SCOPE_SELF_ORG = "7";

    @Autowired
    private ISysRoleService sysRoleService;

    @Pointcut("@annotation(com.zzedu.common.annotation.DataScope)")
    public void pointCut() {
    }

    @Before(value = "pointCut()")
    public void doBefore(JoinPoint point) {
        try {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            DataScope dataScope = AnnotationUtils.findAnnotation(method, DataScope.class);
            if (dataScope == null) {
                dataScope = AnnotationUtils.findAnnotation(method.getDeclaringClass(), DataScope.class);
            }
            if (dataScope != null) {
                handleDataScope(point, dataScope);
            }
        } catch (Exception e) {
            logger.error("数据权限处理异常", e);
        }
    }

    @AfterReturning(pointcut = "pointCut()")
    public void doAfterReturning() {
        DataFilterScopeContext.clearDataFilterScope();
    }

    @AfterThrowing(pointcut = "pointCut()", throwing = "e")
    public void doAfterThrowing(Throwable e) {
        DataFilterScopeContext.clearDataFilterScope();
        logger.error("数据权限处理异常", e);
    }

    protected void handleDataScope(JoinPoint joinPoint, DataScope dataScope) {
        SysUser currentUser = SecurityUtils.getSysUser();
        if (currentUser != null && !currentUser.isAdmin()) {
            Expression filterExpression = dataScopeFilter(currentUser, dataScope);
            DataFilterScopeContext.setDataFilterScope(filterExpression);
        }
    }

    public Expression dataScopeFilter(SysUser user, DataScope filterDataScope) {
        Expression expression = null;

        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<SysRole> roles = Lists.newArrayList();
        if (UserTypeEnumns.PLATFORM == loginUser.getUserTypeEnumns() || UserTypeEnumns.SCHOOL == loginUser.getUserTypeEnumns()) {
            roles = sysRoleService.selectRolesByUserId(user.getId());
        }
        if (CollectionUtils.isEmpty(roles)) {
            return null;
        }
        for (SysRole role : roles) {
            String dataScope = String.valueOf(role.getDataScope());
            switch (dataScope) {
                case DATA_SCOPE_ALL:
                    return null;
                case DATA_SCOPE_CUSTOM:
                    expression = buildCustomDataScope(expression, role, filterDataScope);
                    break;
                case DATA_SCOPE_DEPT:
                    expression = buildDeptDataScope(expression, user, filterDataScope);
                    break;
                case DATA_SCOPE_DEPT_AND_CHILD:
                    expression = buildDeptAndChildDataScope(expression, user, filterDataScope);
                    break;
                case DATA_SCOPE_SELF:
                    expression = buildSelfDataScope(expression, user, filterDataScope);
                    break;
                case DATA_SCOPE_SELF_SCHOOL:
                    expression = buildSelfSchoolDataScope(expression, user, filterDataScope);
                    break;
                case DATA_SCOPE_SELF_ORG:
                    expression = buildSelfOrgDataScope(expression, user, filterDataScope);
                    break;
            }
        }
        return expression != null ? new ParenthesedExpressionList(expression) : null;
    }

    private Expression buildCustomDataScope(Expression expression, SysRole role, DataScope dataScope) {
        InExpression inExpression = new InExpression();
        inExpression.setLeftExpression(buildColumn(dataScope.deptAlias(), dataScope.deptIdColumnName()));

        PlainSelect subSelect = new PlainSelect();
        subSelect.setSelectItems(Collections.singletonList(new SelectItem(new Column("dept_id"))));
        subSelect.setFromItem(new Table("sys_role_dept"));

        EqualsTo roleCondition = new EqualsTo();
        roleCondition.setLeftExpression(new Column("role_id"));
        roleCondition.setRightExpression(new LongValue(role.getId()));
        subSelect.setWhere(roleCondition);

        inExpression.setRightExpression(new ParenthesedExpressionList(subSelect));
        return combineExpressions(expression, inExpression);
    }

    private Expression buildDeptDataScope(Expression expression, SysUser user, DataScope dataScope) {
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(buildColumn(dataScope.deptAlias(), dataScope.deptIdColumnName()));
        equalsTo.setRightExpression(new LongValue(user.getDeptId()));
        return combineExpressions(expression, equalsTo);
    }

    private Expression buildDeptAndChildDataScope(Expression expression, SysUser user, DataScope dataScope) {
        InExpression inExpression = new InExpression();
        inExpression.setLeftExpression(buildColumn(dataScope.deptAlias(), dataScope.deptIdColumnName()));

        PlainSelect subSelect = new PlainSelect();
        subSelect.setSelectItems(Collections.singletonList(new SelectItem(new Column("dept_id"))));
        subSelect.setFromItem(new Table("sys_dept"));

        // 部门ID直接匹配条件
        EqualsTo deptCondition = new EqualsTo();
        deptCondition.setLeftExpression(new Column("dept_id"));
        deptCondition.setRightExpression(new LongValue(user.getDeptId()));

        // 构建字符串包含检查表达式：(',201,', ','+ancestors+',') > 0
        GreaterThan containsCheck = new GreaterThan();

        // 构建第一个参数：',201,'
        StringValue searchPattern = new StringValue("," + user.getDeptId() + ",");

        // 构建第二个参数：','+ancestors+','
        Function concatFunc = new Function();
        concatFunc.setName("concat");
        concatFunc.setParameters(new ExpressionList(
                new StringValue(","),
                new Column("ancestors"),
                new StringValue(",")
        ));

        // 构建完整的定位函数
        Function locateFunc = new Function();
        locateFunc.setName("locate");
        locateFunc.setParameters(new ExpressionList(
                searchPattern,
                concatFunc
        ));

        // 最终表达式：locate(',201,', concat(',', ancestors, ',')) > 0
        containsCheck.setLeftExpression(locateFunc);
        containsCheck.setRightExpression(new LongValue(0));

        // 组合条件
        OrExpression orExpr = new OrExpression(deptCondition, containsCheck);
        subSelect.setWhere(orExpr);

        inExpression.setRightExpression(new ParenthesedExpressionList(subSelect));
        return combineExpressions(expression, inExpression);
    }

    private Expression buildSelfDataScope(Expression expression, SysUser user, DataScope dataScope) {
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(buildColumn("", dataScope.userIdColumnName()));
        equalsTo.setRightExpression(new LongValue(user.getId()));
        return combineExpressions(expression, equalsTo);
    }

    private Expression buildSelfSchoolDataScope(Expression expression, SysUser user, DataScope dataScope) {
        InExpression inExpression = new InExpression();
        inExpression.setLeftExpression(buildColumn("", dataScope.otherIdColumnName()));
        PlainSelect subSelect = new PlainSelect();
        subSelect.setSelectItems(Collections.singletonList(new SelectItem(new Column("id"))));
        subSelect.setFromItem(new Table("zz_school"));
        SysUser sysUser = SecurityUtils.getSysUser();
        EqualsTo idCondition = new EqualsTo();
        idCondition.setLeftExpression(new Column("id"));
        idCondition.setRightExpression(new LongValue(sysUser.getUserTypeId()));

        EqualsTo deletedCondition = new EqualsTo();
        deletedCondition.setLeftExpression(new Column("deleted"));
        deletedCondition.setRightExpression(new LongValue(CommonEnums.YES.getCode()));

/*        EqualsTo stateCondition = new EqualsTo();
        stateCondition.setLeftExpression(new Column("state"));
        stateCondition.setRightExpression(new LongValue(CommonEnums.YES.getCode()));
        subSelect.setWhere(new AndExpression(new AndExpression(idCondition, deletedCondition), stateCondition));
        */
        subSelect.setWhere(new AndExpression(idCondition, deletedCondition));
        inExpression.setRightExpression(new ParenthesedExpressionList(subSelect));
        return combineExpressions(expression, inExpression);
    }

    private Expression buildSelfOrgDataScope(Expression expression, SysUser user, DataScope dataScope) {
        InExpression inExpression = new InExpression();
        inExpression.setLeftExpression(buildColumn("", dataScope.otherIdColumnName()));
        PlainSelect subSelect = new PlainSelect();
        subSelect.setSelectItems(Collections.singletonList(new SelectItem(new Column("id"))));
        subSelect.setFromItem(new Table("zz_school"));
        SysUser sysUser = SecurityUtils.getSysUser();
        EqualsTo idCondition = new EqualsTo();
        idCondition.setLeftExpression(new Column("id"));
        idCondition.setRightExpression(new LongValue(sysUser.getUserTypeId()));

        EqualsTo deletedCondition = new EqualsTo();
        deletedCondition.setLeftExpression(new Column("deleted"));
        deletedCondition.setRightExpression(new LongValue(CommonEnums.YES.getCode()));

/*        EqualsTo stateCondition = new EqualsTo();
        stateCondition.setLeftExpression(new Column("state"));
        stateCondition.setRightExpression(new LongValue(CommonEnums.YES.getCode()));

        subSelect.setWhere(new AndExpression(new AndExpression(idCondition, deletedCondition), stateCondition));*/
        subSelect.setWhere(new AndExpression(idCondition, deletedCondition));
        inExpression.setRightExpression(new ParenthesedExpressionList(subSelect));
        return combineExpressions(expression, inExpression);
    }

    private Expression combineExpressions(Expression existing, Expression newExpr) {
        return existing != null ? new OrExpression(existing, newExpr) : newExpr;
    }

    private Column buildColumn(String tableAlias, String columnName) {
        return new Column(StringUtils.isNotEmpty(tableAlias) ? tableAlias + "." + columnName : columnName);
    }
}